package com.syh.jn.scm.domain.downstream.entity.purchase;

import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.util.other.ExtAssert;
import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.util.validator.ValidationUtils;
import cn.redscarf.commons.wrapper.Restrictions;
import cn.redscarf.commons.wrapper.Wrapper;
import com.syh.core.ddd.domain.AbstractSimpleAggregate;
import com.syh.core.ddd.domain.CodeExpEnum;
import com.syh.core.ddd.domain.CodeGenerateUtil;
import com.syh.jn.scm.domain.downstream.entity.logistics.DownLogisticsBill;
import com.syh.jn.scm.domain.downstream.repository.logistics.DownLogisticsBillReadRepository;
import com.syh.jn.scm.dto.downstream.purchase.DownPurchaseOrderMto;
import com.syh.jn.scm.dto.downstream.purchase.DownPurchaseOrderSto;
import com.syh.jn.scm.dto.support.FilesDto;
import com.syh.jn.scm.exception.BusinessException;
import com.syh.jn.scm.service.api.downstream.logistics.enums.DownLogisticsBillEnums;
import com.syh.jn.scm.service.api.downstream.purchase.enums.DownPurchaseOrderEnums;
import com.syh.jn.scm.service.api.supplier.logistics.enums.LogisticsBillEnums;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Date:2018/7/3
 * Author:Clown
 * 下游采购单聚合;
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class DownPurchaseOrder extends AbstractSimpleAggregate<Long> {

  public DownPurchaseOrder() {
  }

  public DownPurchaseOrder(Long id) {
    this.id = id;
  }

  /**
   * 下游创建新采购单
   *
   * @return
   */
  public static DownPurchaseOrder createByDownStream(DownPurchaseOrderSto sto) {
    ValidationUtils.validate(sto);
    //销售单由下游创建创建类型为1
    DownPurchaseOrder order = commonCreatedPurchaseOrder(Short.valueOf("1"));
    order.setCode(CodeGenerateUtil.codeGenerate(CodeExpEnum.SALES_ORDER_EXP.getCodeExp(), sto.getDownStreamCompanyId()));
    order.setUuid(UUID.randomUUID().toString());
    ExtAssert.notNull(sto.getItems(), "明细对象为空");
    ExtBeanUtils.copyProperties(sto, order, false);
    //作为下游并不能设置订单状态和采购单状态，所以不用设置
    //下面的循环主要是为了计算订单总额
    order.items = new ArrayList<>();
    sto.getItems().stream().forEach(itemsto -> {
      ExtAssert.notNull(itemsto, "商品对象为空");
      ValidationUtils.validate(itemsto);
      order.addItem(new DownPurchaseOrderItem(itemsto));
    });
    //计算订单总额
    order.calculateTotalAmount();

    return order;
  }

  /**
   * 下游修改采购单
   *
   * @param mto
   */
  public void modify(DownPurchaseOrderMto mto) {
    ExtAssert.notNull(mto, "对象为空");
    //采购单已确认则不可修改
    if (!this.getOrdState().equals(DownPurchaseOrderEnums.OrdState.STAYCONFIRM.getStatus())
            && !this.getOrdState().equals(DownPurchaseOrderEnums.OrdState.CANCEL.getStatus())) {
      throw new BusinessException("订单已经确认,不可以修改");
    }
    ExtBeanUtils.copyProperties(mto, this, false);
    this.items = new ArrayList<>();
    mto.getItems().stream().forEach(itemMto -> {
      this.addItem(new DownPurchaseOrderItem(itemMto));
    });
    this.calculateTotalAmount();
  }

  /**
   * 下游采购订单上传图片
   *
   * @param dto
   */
  public void upload(FilesDto dto) {
    this.secondSrcImages = dto.getFiles();
  }

  /**
   * 创建订单
   *
   * @param createdType 创建订单来源
   * @return
   */
  private static DownPurchaseOrder commonCreatedPurchaseOrder(Short createdType) {
    DownPurchaseOrder order = new DownPurchaseOrder();
    order.ordState = DownPurchaseOrderEnums.OrdState.STAYCONFIRM.getStatus();
    order.generatedState = DownPurchaseOrderEnums.GeneratedState.LOGISTICS_UN_GENERATE.getStatus();
    order.deliveryState = DownPurchaseOrderEnums.DeliveryState.LOGISTICS_UN_DELIVERY.getStatus();
    order.collectState = DownPurchaseOrderEnums.CollectState.LOGISTICS_UN_COLLECT.getStatus();
    order.createdType = createdType;
    order.createTimeStamp = System.currentTimeMillis();
    order.inputDateTimestamp = System.currentTimeMillis();
    order.discount = BigDecimal.ZERO;
    return order;
  }

  /**
   * 增加下游采购订单项
   *
   * @param item
   * @Return
   */
  public DownPurchaseOrder addItem(DownPurchaseOrderItem item) {
    if (this.items == null) {
      this.items = new ArrayList<>();
    }
    this.items.add(item);
    return this;
  }

  /**
   * 计算采购单商品总额和订单总额
   */
  public void calculateTotalAmount() {
    BigDecimal totalAmount = BigDecimal.ZERO;
    for (DownPurchaseOrderItem item : this.items) {
      totalAmount = totalAmount.add(item.getTotalAmount());
    }
    this.goodsTotalAmount = totalAmount;//商品总额
    this.totalAmount = totalAmount;//订单总额
    if (this.discount != null) {
      this.totalAmount = this.totalAmount.subtract(discount);
    }
  }

  /**
   * 计算采购单商品收货总额和订单收货总额
   */
  public void calculateTotalInAmount() {
    BigDecimal totalAmount = BigDecimal.ZERO;
    //计算入库商品总额
    for (DownPurchaseOrderItem item : this.items) {
      totalAmount = totalAmount.add(item.calculateTotalInAmount());
    }
    this.totalInGoodsAmount = totalAmount;

    //先计算出该订单下已签收状态的物流单据的物流费用
    DownLogisticsBillReadRepository logisticsBillReadRepository = (DownLogisticsBillReadRepository) SpringContextUtils.getBean(DownLogisticsBillReadRepository.class);
    Wrapper we = Wrapper.build();
    we.append(Restrictions.eq("orderId", this.id));
    we.append(Restrictions.eq("orderType", DownLogisticsBillEnums.OrderType.PURCHASE.getStatus()));
    we.append(Restrictions.eq("logisticsState", DownLogisticsBillEnums.State.SIGNIN.getStatus()));
    List<DownLogisticsBill> logisticsBills = logisticsBillReadRepository.getList(we);

    BigDecimal logisticsBillBeforeBuyFreight = BigDecimal.ZERO;//物流单据为预收买家的所有物流费用
    for (DownLogisticsBill logisticsBill : logisticsBills) {
      if (logisticsBill.getFreightTake() != null && logisticsBill.getFreightTake().equals(LogisticsBillEnums.FreightTake.BUY.getStatus())
              && logisticsBill.getFreightType().equals(LogisticsBillEnums.FreightType.BEFORE.getStatus()))
        logisticsBillBeforeBuyFreight = logisticsBillBeforeBuyFreight.add(logisticsBill.getTotalFreight());
    }

    //加上预收买家的物流运费
    totalAmount = totalAmount.add(logisticsBillBeforeBuyFreight);
    //减去优惠价格
    if (this.discount != null) {
      totalAmount = totalAmount.subtract(this.discount);
    }
    //加上客户承担费用
    if (this.otherExpenses != null) {
      totalAmount = totalAmount.add(this.otherExpenses);
    }
    //入库总额
    this.totalInAmount = totalAmount.compareTo(BigDecimal.ZERO) < 1 ? BigDecimal.ZERO : totalAmount;
    this.lastUpdateTimeStamp = System.currentTimeMillis();
  }

  /**
   * 计算采购单商品收货状态
   */
  public void calculateCollectStatus() {
    //默认为已完全签收
    this.collectState = DownPurchaseOrderEnums.CollectState.LOGISTICS_WHOLE_COLLECT.getStatus();
    //默认标记为未生成物流单
    boolean unCollected = true;

    for (DownPurchaseOrderItem item : this.items) {
      //订单项已生成数量小于订单数量则为部分生成物流单
      if (this.collectState.equals(DownPurchaseOrderEnums.CollectState.LOGISTICS_WHOLE_COLLECT.getStatus())
              && item.getInQuantity().compareTo(item.getQuantity()) < 0) {
        this.collectState = DownPurchaseOrderEnums.CollectState.LOGISTICS_PART_COLLECT.getStatus();
      }
      //订单项已发货数量大于0 则不是未发货
      if (unCollected && item.getInQuantity().compareTo(BigDecimal.ZERO) > 0) {
        unCollected = false;
      }
    }
    if (unCollected) {
      this.collectState = DownPurchaseOrderEnums.CollectState.LOGISTICS_UN_COLLECT.getStatus();
      this.receiptTimestamp = null;
    }
  }

  /**
   * 确认收货
   */
  public void collect(DownLogisticsBill bill) {
    if (this.collectState.equals(DownPurchaseOrderEnums.CollectState.LOGISTICS_WHOLE_COLLECT.getStatus()))
      throw new BusinessException("签收失败，已全部签收！");
    //修改订单项发货数量
    bill.getItems().forEach(logisticsBillItem -> {
      this.items.forEach(orderItem -> {
        if (orderItem.getId().equals(logisticsBillItem.getOrderItemId())) {
          orderItem.setInQuantity(orderItem.getInQuantity() == null ? logisticsBillItem.getQuantity() :
                  orderItem.getInQuantity().add(logisticsBillItem.getQuantity()));
        }
      });
    });
    this.receiptTimestamp = System.currentTimeMillis();
    this.calculateTotalInAmount();
    this.calculateCollectStatus();
  }

  /**
   * 取消收货
   */
  public void cancelCollect(DownLogisticsBill bill) {
    if (this.collectState.equals(DownPurchaseOrderEnums.CollectState.LOGISTICS_UN_COLLECT.getStatus()))
      throw new BusinessException("取消签收失败，无签收记录！");
    //修改订单项发货数量
    bill.getItems().forEach(logisticsBillItem -> {
      this.items.forEach(orderItem -> {
        if (orderItem.getId().equals(logisticsBillItem.getOrderItemId())) {
          if (orderItem.getGeneratedReturnQuantity() != null && orderItem.getGeneratedReturnQuantity().compareTo(BigDecimal.ZERO) > 0) {
            throw new BusinessException(orderItem.getGoodsName()+"已生成退货单，无法取消签收！");
          }
          orderItem.setInQuantity(orderItem.getInQuantity().subtract(logisticsBillItem.getQuantity()));
        }
      });
    });
    this.calculateTotalInAmount();
    this.calculateCollectStatus();
  }

  /**
   * 取消采购单
   */
  public void cancel() {
    //只有订单状态为待确认的订单才能取消
    if (!this.getOrdState().equals(DownPurchaseOrderEnums.OrdState.STAYCONFIRM.getStatus())) {
      logger.error("cancel:采购单状态不为待确认，不能取消");
      throw new RuntimeException("cancel:采购单状态不为待确认，不能取消");
    }
    this.ordState = DownPurchaseOrderEnums.OrdState.CANCEL.getStatus();
  }

  /**
   * 删除订单
   */
  public void destroy() {
    //只有订单状态为待确认或者已取消的才能删除
    if (!this.getOrdState().equals(DownPurchaseOrderEnums.OrdState.CANCEL.getStatus())) {
      throw new BusinessException("订单不是待确认或已取消状态,不可以删除");
    }
    if (this.haveRecord != null &&
            this.haveRecord.equals(DownPurchaseOrderEnums.HaveRecord.HAVE_RECORD.getStatus())) {
      throw new BusinessException("订单已存在记录,不可以删除");
    }
  }

  /**
   * 创建退货单 记录退货数量
   * @param returnOrder 退货单
   */
  public void createdReturnOrder(DownPurchaseReturnOrder returnOrder) {
    this.addGeneratedReturnQuantity(returnOrder);
  }

  /**
   * 修改退货单 记录退货数量
   * @param returnOrder 退货单
   * @param oldReturnOrder 旧退货单
   */
  public void updatedReturnOrder(DownPurchaseReturnOrder returnOrder,DownPurchaseReturnOrder oldReturnOrder) {
   this.subGeneratedReturnQuantity(oldReturnOrder);
   this.addGeneratedReturnQuantity(returnOrder);
  }

  /**
   * 取消退货单 记录退货数量
   * @param returnOrder 退货单
   */
  public void cancelReturnOrder(DownPurchaseReturnOrder returnOrder) {
    this.subGeneratedReturnQuantity(returnOrder);
  }

  /**
   * 添加退货数量
   * @param returnOrder
   */
  private void addGeneratedReturnQuantity(DownPurchaseReturnOrder returnOrder) {
    returnOrder.getItems().forEach(returnOrderItem -> {
      this.items.forEach(orderItem->{
        if (orderItem.getId().equals(returnOrderItem.getOrderItemId())) {
          //记录退货数量
          orderItem.setGeneratedReturnQuantity(orderItem.getGeneratedReturnQuantity() == null ? returnOrderItem.getReturnQuantity() : orderItem.getGeneratedReturnQuantity().add(returnOrderItem.getReturnQuantity()));
          //判断退货数量是否大于签收数量
          if (orderItem.getGeneratedReturnQuantity().compareTo(orderItem.getInQuantity()) > 0) {
            BigDecimal returnQuantity = orderItem.getGeneratedReturnQuantity().subtract(returnOrderItem.getReturnQuantity());
            throw new BusinessException("退货数量不能大于签收数量！"+orderItem.getGoodsName()+"已签收"+orderItem.getInQuantity()+" 已生成退货："+returnQuantity);
          }
        }
      });
    });
  }

  /**
   * 减少退货数量
   * @param returnOrder
   */
  private void subGeneratedReturnQuantity(DownPurchaseReturnOrder returnOrder) {
    returnOrder.getItems().forEach(returnOrderItem -> {
      this.items.forEach(orderItem->{
        if (orderItem.getId().equals(returnOrderItem.getOrderItemId())) {
          //回退退货数量
          orderItem.setGeneratedReturnQuantity(orderItem.getGeneratedReturnQuantity().subtract(returnOrderItem.getReturnQuantity()));
          //判断回退后的数量是否小于0  正常情况不应该小于0
          if (orderItem.getGeneratedReturnQuantity().compareTo(BigDecimal.ZERO) < 0) {
            throw new BusinessException("服务异常，请联系客服！");
          }
        }
      });
    });
  }

  /**
   * 生成对账单
   */
  public void generating(Long statementOfAccountId) {
    if (this.collectState.equals(DownPurchaseOrderEnums.CollectState.LOGISTICS_UN_COLLECT.getStatus())) {
      throw new BusinessException("采购单" + this.code + "未签收，无法生成对账单");
    }
    if (this.ordState.equals(DownPurchaseOrderEnums.OrdState.STATEMENT.getStatus())) {
      throw new BusinessException("采购单已生成对账单!");
    }
    this.statementId = statementOfAccountId;
    this.ordState = DownPurchaseOrderEnums.OrdState.STATEMENT.getStatus();
  }

  /**
   * 取消对账单
   */
  public void cancelGenerating() {
    if (!this.ordState.equals(DownPurchaseOrderEnums.OrdState.STATEMENT.getStatus())) {
      throw new BusinessException("采购单已取消对账单!");
    }
    this.statementId = null;
    this.ordState = DownPurchaseOrderEnums.OrdState.CONFIRM.getStatus();
  }

  /**
   * 销售订单编号
   */
  private String code;
  /**
   * 第三方订单号
   */
  private String extCode;
  /**
   * 销售订单状态(0：待确认 1：已确认 2：挂起 3：已生成对账单 -1：已取消)
   */
  private Short ordState;
  /**
   * 订单创建类型( 0：服务商录入 1:客户录入 2：对接客户系统 3：系统自动)
   */
  private Short createdType;
  /**
   * 客户id
   */
  private Long downStreamCompanyId;
  /**
   * 归属id
   */
  private Long businessEntityId;
  /**
   * 订单备注
   */
  private String remark;
  /**
   * 优惠金额
   */
  private BigDecimal discount;
  /**
   * 订单总额
   */
  private BigDecimal totalAmount;
  //TODO 下游收货总额字段预计以后要删除
  /**
   * 下游收货总额
   */
  private BigDecimal totalInAmount;
  //TODO 下游收货商品总额字段预计以后要删除
  /**
   * 下游收货商品总额
   */
  private BigDecimal totalInGoodsAmount;
  /**
   * 出库总额
   */
  private BigDecimal totalOutAmount;
  /**
   * 出库商品总额
   */
  private BigDecimal totalOutGoodsAmount;
  /**
   * 运费金额
   */
  private BigDecimal totalFreight;
  /**
   * 发货地区编码
   */
  private String shipAreaCode;
  /**
   * 发货地址省 市 区
   */
  private String shipArea;
  /**
   * 发货人详细地址
   */
  private String shipAddress;
  /**
   * 录入日期
   */
  private Long inputDateTimestamp;
  /**
   * 收货时间
   */
  private Long receiptTimestamp;
  /**
   * 录单人员id
   */
  private Long operatorId;
  /**
   * 收货人姓名
   */
  private String deliName;
  /**
   * 收货人联系电话
   */
  private String deliTel;
  /**
   * 收货地区编码
   */
  private String deliAreaCode;
  /**
   * 收货地址省 市 区
   */
  private String deliArea;
  /**
   * 收货人详细地址
   */
  private String deliAddress;
  /**
   * 出库时间
   */
  private Long deliveryTimestamp;
  /**
   * 要求到货时间
   */
  private Long receiveTimestamp;
  /**
   * 订单账期
   */
  private Integer accountPeriod;
  /**
   * 物流生成状态(0：未生成物流单 1：部分生成物流单2：完成生成物流单)
   */
  private Short generatedState;
  /**
   * 发货状态(0：未发货 1：部分发货 2：完成发货)
   */
  private Short deliveryState;
  /**
   * 签收状态(0：未签收 1：部分签收 2：完成签收)
   */
  private Short collectState;
  /**
   * 商品总额
   */
  private BigDecimal goodsTotalAmount;
  /**
   * 对应对账单id
   */
  private Long statementId;
  /**
   * 我方单据图片
   */
  private String mainSrcImages;
  /**
   * 对方单据图片
   */
  private String secondSrcImages;
  /**
   * 是否已生成记录(库存记录、应收付记录等) 0:未生成 1：已生成
   */
  private Short haveRecord;
  /**
   * 客户承担费用
   */
  private BigDecimal otherExpenses;
  /**
   * 订单明细
   */
  protected List<DownPurchaseOrderItem> items;
}
